#include <string.h>
#include <stdint.h>
#include <stdlib.h>
#include <stdio.h>

#include <psp2/system_param.h>
#include <psp2/ctrl.h>

#include <SDL2/SDL.h>
#include <SDL2/SDL_image.h>
#include <SDL2/SDL_mixer.h>
#include <SDL2/SDL_ttf.h>

#include "utils.h"
#include "patch.h"

#define JBUTTON_TRIANGLE 0
#define JBUTTON_CIRCLE 1
#define JBUTTON_CROSS 2
#define JBUTTON_SQUARE 3
#define JBUTTON_LTRIGGER 4
#define JBUTTON_RTRIGGER 5
#define JBUTTON_DOWN 6
#define JBUTTON_LEFT 7
#define JBUTTON_UP 8
#define JBUTTON_RIGHT 9
#define JBUTTON_SELECT 10
#define JBUTTON_START 11

#define CBUTTON_CROSS 0
#define CBUTTON_CIRCLE 1
#define CBUTTON_SQUARE 2
#define CBUTTON_TRIANGLE 3
#define CBUTTON_SELECT 4
#define CBUTTON_START 6
#define CBUTTON_LTRIGGER 9
#define CBUTTON_RTRIGGER 10
#define CBUTTON_UP 11
#define CBUTTON_DOWN 12
#define CBUTTON_LEFT 13
#define CBUTTON_RIGHT 14

static int cbutton_enter = CBUTTON_CIRCLE, cbutton_cancel = CBUTTON_CROSS;

SDL_Window *SDL_CreateWindow_soloader(const char *title, int x, int y, int w, int h, Uint32 flags)
{
	SDL_Window *window = SDL_CreateWindow(title, x, y, w, h, flags);
	debugPrintf("SDL_CreateWindow: title: %s, x:%d, y:%d, w:%d, h:%d, flags:0x%x; return: %p\n", title, x, y, w, h, flags, window);

	return window;
}

SDL_Renderer *SDL_CreateRenderer_soloader(SDL_Window *window, int index, Uint32 flags)
{
	SDL_Renderer *renderer = SDL_CreateRenderer(window, index, flags);
	debugPrintf("SDL_CreateRenderer: window: %p, index:%d, flags:0x%x; return: %p\n", window, index, flags, renderer);

	return renderer;
}

void SDL_RenderPresent_soloader(SDL_Renderer *renderer)
{
	static int bFirstPresent = 1;
	static unsigned int old_buttons = 0;

	if (bFirstPresent)
	{
		// debugPrintf("SDL_RenderPresent first called.\n", renderer);
		bFirstPresent = 0;
		return;
	}

	SceCtrlData pad;
	memset(&pad, 0, sizeof(SceCtrlData));
	sceCtrlPeekBufferPositive(0, &pad, 1);

	if ((pad.buttons & SCE_CTRL_SELECT) && !(old_buttons & SCE_CTRL_SELECT))
	{
		setting_config.full_screen = !setting_config.full_screen;
		RenderFullScreen(setting_config.full_screen);
		saveSettingConfig(&setting_config);
	}
	old_buttons = pad.buttons;

	SDL_RenderPresent(renderer);
}

void SDL_FreeSurface_soloader(SDL_Surface *surface)
{
	if (!surface)
		return;

	if (surface->flags & SDL_DONTFREE2)
		free(surface);
	else
		SDL_FreeSurface(surface);
}

int SDL_UpperBlit_soloader(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
{
	if (!src || !dst)
		return -1;

	return SDL_UpperBlit(src, srcrect, dst, dstrect);
}

int SDL_UpperBlitScaled_soloader(SDL_Surface *src, const SDL_Rect *srcrect, SDL_Surface *dst, SDL_Rect *dstrect)
{
	if (!src || !dst)
		return -1;

	return SDL_UpperBlit(src, srcrect, dst, dstrect);
}

int SDL_UpdateTexture_soloader(SDL_Texture *texture, const SDL_Rect *rect, const void *pixels, int pitch)
{
	if (!texture || !pixels)
		return -1;

	return SDL_UpdateTexture(texture, rect, pixels, pitch);
}

int SDL_RenderCopy_soloader(SDL_Renderer *renderer, SDL_Texture *texture, const SDL_Rect *srcrect, const SDL_Rect *dstrect)
{
	return SDL_RenderCopy(renderer, texture, srcrect, dstrect);
}

SDL_Texture *SDL_CreateTextureFromSurface_soloader(SDL_Renderer *renderer, SDL_Surface *surface)
{
	return SDL_CreateTextureFromSurface(renderer, surface);
}

int SDL_PollEvent_soloader(SDL_Event *event)
{
	int ret = SDL_PollEvent(event);
	if (ret > 0)
	{
		if (event->type == SDL_CONTROLLERBUTTONDOWN || event->type == SDL_CONTROLLERBUTTONUP)
		{
			if (event->cbutton.button == 4)				// Select
				event->cbutton.button = -1;				// disable
			else if (event->cbutton.button == 6)		// Start
				event->cbutton.button = -1;				// disable
			else if (event->cbutton.button == 0)		// A
				event->cbutton.button = cbutton_enter;	// convert
			else if (event->cbutton.button == 1)		// B
				event->cbutton.button = cbutton_cancel; // convert
		}
	}

	return ret;
}

int closeMemRw(SDL_RWops *context)
{
	if (context)
	{
		if (context->hidden.mem.base)
			free(context->hidden.mem.base);
		free(context);
		debugPrintf("RWopsMem close: %p\n", context);
	}

	return 0;
}

SDL_RWops *Get_RWops(const char *filePath)
{
	SDL_RWops *rwops = NULL;

	// Mp3文件整块读入缓存以解决可能因IO堵塞造成的声音卡顿等问题
	char *p = strrchr(filePath, '.');
	if (p++ && strcasecmp(p, "mp3") == 0)
	{
		void *mem = NULL;
		int size = allocateReadFile(filePath, &mem);
		if (size <= 0)
		{
			if (mem)
				free(mem);
			return NULL;
		}
		rwops = SDL_RWFromMem(mem, size);
		if (!rwops)
		{
			if (mem)
				free(mem);
			return NULL;
		}
		rwops->close = closeMemRw;
		debugPrintf("Get_RWopsMem: path: %s, return: %p\n", filePath, rwops);
		return rwops;
	}

	rwops = SDL_RWFromFile(filePath, "rb");
	debugPrintf("Get_RWops: path: %s, return: %p\n", filePath, rwops);
	return rwops;
}

static void setButtons()
{
	if (enter_button == SCE_SYSTEM_PARAM_ENTER_BUTTON_CIRCLE)
	{
		cbutton_enter = CBUTTON_CIRCLE;
		cbutton_cancel = CBUTTON_CROSS;
	}
	else
	{
		cbutton_enter = CBUTTON_CROSS;
		cbutton_cancel = CBUTTON_CIRCLE;
	}
}

int initPatchSdl()
{
	setButtons();

	hook_addr(so_symbol(&so_mod, "_Z9Get_RWopsPKc"), (uintptr_t)&Get_RWops);

	return 0;
}
